package utils;

import java.io.*;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

import javax.mail.BodyPart;
import javax.mail.Header;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Part;

import com.sun.mail.util.BASE64DecoderStream;

import static javax.mail.internet.MimeUtility.decodeText;

public class EmlMessage {

    private Message message;

    private List<Content> content;

    public EmlMessage(Message message) {
        this.message = message;
    }

    @SuppressWarnings("unchecked")
    public Enumeration<Header> headers() throws MessagingException {
        return message.getAllHeaders();
    }

    public Message getMessage() {
        return message;
    }

    public List<Content> getContent() throws IOException, MessagingException {
        if (content != null) {
            return content;
        }
        content = new ArrayList<Content>();
        parsePart(message, content);
        return content;

    }

    public List<Content> getContent(String dir) throws IOException, MessagingException {
        if (content != null) {
            return content;
        }
        content = new ArrayList<Content>();
        parsePart(message, content,dir);
        return content;

    }

    public static class Content {

        public static final String CONTENT_TYPE_HTML = "text/html";
        public static final String CONTENT_TYPE_PLAIN = "text/plain";
        public static final String CONTENT_IMAGE="image";
        public static final String CONTENT_TYPE_OCTET_STREAM = "application/octet-stream";

        private String contentType;
        private String body;
        private Part part;
        private BASE64DecoderStream base64Stream;

        public Content(Part part) throws MessagingException, IOException {
            this.part = part;
            this.contentType = part.getContentType();
            if (isHtml() || isTextPlain()) {
                this.body = (String) part.getContent();
            }else {
                this.base64Stream = (BASE64DecoderStream) part.getContent();
            }
        }
        public Content(Part part,String dir) throws MessagingException, IOException {
            this.part = part;
            this.contentType = part.getContentType();
            if (isHtml() || isTextPlain()) {
                this.body = (String) part.getContent();
            } else if(isOctetStream()){
                saveAttachment(part,dir);
            }else if(isImage()){
                saveImage(part,dir);
            }else {
                this.base64Stream = (BASE64DecoderStream) part.getContent();
            }
        }

        public BASE64DecoderStream getBase64Stream() {
            return base64Stream;
        }

        public void setBase64Stream(BASE64DecoderStream base64Stream) {
            this.base64Stream = base64Stream;
        }

        public String getContentType() {
            return contentType;
        }

        public String getBody() {
            return body;
        }

        public Part getPart() {
            return part;
        }

        public boolean isTextPlain() {
            return contentType.startsWith(CONTENT_TYPE_PLAIN);
        }

        public boolean isHtml() {
            return contentType.startsWith(CONTENT_TYPE_HTML);
        }

        public boolean isOctetStream() {
            return contentType.startsWith(CONTENT_TYPE_OCTET_STREAM);
        }

        public boolean isImage(){
            return contentType.split(";")[0].startsWith(CONTENT_IMAGE);
        }

    }

    static void parsePart(Part part, List<Content> list) throws IOException, MessagingException {
        if (part.isMimeType("multipart/*")) {
            Multipart multipart = (Multipart) part.getContent();
            int count = multipart.getCount();
            for (int i = 0; i < count; i++) {
                BodyPart bodyPart = multipart.getBodyPart(i);
                parsePart(bodyPart, list);
            }
            return;
        }
        list.add(new Content(part));
    }

    static void parsePart(Part part, List<Content> list,String dir) throws IOException, MessagingException {
        if (part.isMimeType("multipart/*")) {
            Multipart multipart = (Multipart) part.getContent();
            int count = multipart.getCount();
            for (int i = 0; i < count; i++) {
                BodyPart bodyPart = multipart.getBodyPart(i);
                parsePart(bodyPart, list,dir);
            }
            return;
        }
        list.add(new Content(part,dir));
    }

    static void saveAttachment(Part part,String destDir)throws MessagingException,IOException{
        saveFile(part.getInputStream(),destDir,decodeText(part.getFileName()));
    }

    static void saveImage(Part part,String destDir)throws MessagingException,IOException{
        String name = part.getFileName().substring(0,part.getFileName().lastIndexOf("."));
        saveFile(part.getInputStream(),destDir,name);
    }

    private static void saveFile(InputStream is, String destDir, String fileName)
            throws IOException {
        BufferedInputStream bis = new BufferedInputStream(is);
        BufferedOutputStream bos = new BufferedOutputStream(
                new FileOutputStream(new File(destDir + fileName)));
        int len = -1;
        while ((len = bis.read()) != -1) {
            bos.write(len);
            bos.flush();
        }
        bos.close();
        bis.close();
    }

}
